package llc.ufwa.unlimitedlist;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import llc.ufwa.activities.injecting.InitilizationAwareApplication;
import llc.ufwa.concurrency.DaemonThreadFactory;
import llc.ufwa.data.exception.ResourceException;
import llc.ufwa.data.resource.Converter;
import llc.ufwa.data.resource.SerializingConverter;
import llc.ufwa.data.resource.cache.Cache;
import llc.ufwa.data.resource.cache.DiskCache;
import llc.ufwa.data.resource.cache.ExpiringCache;
import llc.ufwa.data.resource.cache.ListCache;
import llc.ufwa.data.resource.cache.MemoryCache;
import llc.ufwa.data.resource.cache.SynchronizedCache;
import llc.ufwa.data.resource.cache.ValueConvertingCache;
import llc.ufwa.data.resource.loader.CachedParallelResourceLoader;
import llc.ufwa.data.resource.loader.ResourceLoader;
import llc.ufwa.logging.LogcatAppender;
import llc.ufwa.unlimitedlist.activities.list.UnlimitedItemSizeConverter;
import llc.ufwa.unlimitedlist.data.UnlimitedListItem;
import llc.ufwa.unlimitedlist.loader.RootLoader;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import android.content.Context;


public class MainApplication extends InitilizationAwareApplication {
    
   static { 
	   
       org.apache.log4j.Logger root = org.apache.log4j.Logger.getRootLogger();

       root.addAppender(new LogcatAppender("UnlimitedList"));
       
   }
   
   private static final Logger logger = LoggerFactory.getLogger(MainApplication.class);
   private SynchronizedCache<Long, UnlimitedListItem> previousItemsDisk;
   private SynchronizedCache<Long, UnlimitedListItem> previousItemsList;
   private CachedParallelResourceLoader<Long, UnlimitedListItem> previousItems;
   private CachedParallelResourceLoader<Long, UnlimitedListItem> itemsLoader;
 
   public MainApplication() {
   }

   @Override
   public void onCreate() {
       super.onCreate();
       
       final ExecutorService bulkWorkers = Executors.newFixedThreadPool(100, new DaemonThreadFactory());
       final ExecutorService limited = Executors.newFixedThreadPool(4, new DaemonThreadFactory());
       
       final int LIMIT_DEPTH = 20;
       final int LIMIT_CALLS = 15;
           
           {
               final File previousItemsFolder = this.getDir("previous-items", Context.MODE_APPEND);
               final Cache<Long, byte []> diskCache = new DiskCache<Long>(previousItemsFolder, -1L, -1L);

               previousItemsDisk = new SynchronizedCache<Long, UnlimitedListItem>(new ValueConvertingCache<Long, UnlimitedListItem, byte []>(
                       diskCache,
                       new SerializingConverter<UnlimitedListItem>()
                   ));
               
               final Cache<Long, UnlimitedListItem> previousThreadInfosMemory = 
                   new SynchronizedCache<Long, UnlimitedListItem>(
                       new MemoryCache<Long, UnlimitedListItem>(
                               new UnlimitedItemSizeConverter(),
                               50000
                           )
                       );
                   
               final List<Cache<Long, UnlimitedListItem>> listOfCaches = new ArrayList<Cache<Long, UnlimitedListItem>>();
               listOfCaches.add(previousThreadInfosMemory);
               listOfCaches.add(previousItemsDisk);
               
               previousItemsList =
                       new SynchronizedCache<Long, UnlimitedListItem>(new ListCache<Long, UnlimitedListItem>(listOfCaches, true));
               
               final ResourceLoader<Long, UnlimitedListItem> rootPrevious = new ResourceLoader<Long, UnlimitedListItem>() {

                   @Override
                   public boolean exists(
                       final Long key
                   ) throws ResourceException {
                       return previousItemsDisk.exists(key);
                   }

                   @Override
                   public UnlimitedListItem get(
                       final Long key
                   ) throws ResourceException {
                       return previousItemsDisk.get(key);
                   }

                   @Override
                   public List<UnlimitedListItem> getAll(
                       final List<Long> keys
                   ) throws ResourceException {
                       return previousItemsDisk.getAll(keys);
                   }
                   
               };
               
               previousItems = new CachedParallelResourceLoader<Long, UnlimitedListItem>(
                       rootPrevious,
                       new UnlimitedListLimitingExecutorService(limited, bulkWorkers, LIMIT_CALLS),
                       bulkWorkers,
                       bulkWorkers,
                       LIMIT_DEPTH,
                       "previousItemsLoader",
                       previousThreadInfosMemory,
                       createStandardSearchCache()
                   );
               
           }
           
           final RootLoader root = new RootLoader(this);
       
          itemsLoader =
               new CachedParallelResourceLoader<Long, UnlimitedListItem>(
        		   root,
                   new UnlimitedListLimitingExecutorService(limited, bulkWorkers, LIMIT_CALLS),
                   bulkWorkers,
                   bulkWorkers,
                   LIMIT_DEPTH,
                   "itemsLoader",
                   new SynchronizedCache<Long, UnlimitedListItem>(
	                   new ExpiringCache<Long, UnlimitedListItem>(
	                       new MemoryCache<Long, UnlimitedListItem>(
	                           new UnlimitedItemSizeConverter(),
	                           500000 / 2
	                       ), 
	                       10000,
	                       20000
	                   )
	               ),
                   createStandardSearchCache()
               );
           
           
       this.flagInitialized();
       
   }
   
   public static final int SEARCH_CACHE = 10000; //~10kb
   
   private Cache<Long, Boolean> createStandardSearchCache() {
       
       return 
           new SynchronizedCache<Long, Boolean>(
               new MemoryCache<Long, Boolean>(
                   new Converter<Boolean, Integer>() {
   
                       @Override
                       public Integer convert(Boolean old) throws ResourceException {
                           return 1;
                       }
           
                       @Override
                       public Boolean restore(Integer newVal) throws ResourceException {
                           return null;
                       }
                       
                   },
                   SEARCH_CACHE
               )
           );
       
   }
	
	public SynchronizedCache<Long, UnlimitedListItem> getPreviousItemsDisk() {
		return previousItemsDisk;
	}
	
	public SynchronizedCache<Long, UnlimitedListItem> getPreviousItemsList() {
		return previousItemsList;
	}
	
	public CachedParallelResourceLoader<Long, UnlimitedListItem> getPreviousItems() {
		return previousItems;
	}
	
	public CachedParallelResourceLoader<Long, UnlimitedListItem> getItemsLoader() {
		return itemsLoader;
	}
	   
   

}
